#include "WxWidgetsDrawer.h"

using namespace cmn;
using namespace drawing;

unsigned ToChar( float val );
float ToFloat( unsigned val );

wxColor ToWxColor( const Color& color );
Color ToEditorColor( const wxColor& color );

wxPenStyle ToWxPenStyle( PenStyle penStyle );
PenStyle ToEditorPenStyle( wxPenStyle penStyle );

wxPen ToWxPen( const Pen& pen );
Pen ToEditorPen( const wxPen& pen );

BrushStyle ToEditorBrushStyle( wxBrushStyle brushStyle );
wxBrushStyle ToWxBrushStyle( BrushStyle brushStyle );

wxBrush ToWxBrush( const Brush& brush );
Brush ToEditorBrush( const wxBrush& brush );

WxWidgetsDrawer::WxWidgetsDrawer( wxDC& dc, const CoordsTransformer* transformer ) :
	m_dc( dc ),
	m_transformer( transformer )
{}

WxWidgetsDrawer::~WxWidgetsDrawer() {}

Pen WxWidgetsDrawer::GetPen() const {
	return ToEditorPen( m_dc.GetPen() );
}

void WxWidgetsDrawer::SetPen( const Pen& pen ) {
	m_dc.SetPen( ToWxPen( pen ) );
}

Brush WxWidgetsDrawer::GetBrush() const {
	return ToEditorBrush( m_dc.GetBrush() );
}

void WxWidgetsDrawer::SetBrush( const Brush& brush ) {
	m_dc.SetBrush( ToWxBrush( brush ) );
}

void WxWidgetsDrawer::DrawPoint( const Vec2& point ) {
	wxPoint coords = m_transformer->WorldToScreen( point );
	m_dc.DrawPoint( coords );
}

void WxWidgetsDrawer::DrawLine( const Vec2& start, const Vec2& end ) {
	wxPoint point0 = m_transformer->WorldToScreen( start );
	wxPoint point1 = m_transformer->WorldToScreen( end );

	m_dc.DrawLine( point0, point1 );
}

void WxWidgetsDrawer::DrawCircle( const Circle& circle ) {
	Vec2 circlePoint = circle.Center + Vec2::Right() * circle.Radius;

	wxPoint screenCenter = m_transformer->WorldToScreen( circle.Center );
	wxPoint screenPoint = m_transformer->WorldToScreen( circlePoint );
	wxPoint screenRadiusVect = screenPoint - screenCenter;

	float screenRadius = sqrt( screenRadiusVect.x * screenRadiusVect.x + screenRadiusVect.y * screenRadiusVect.y );
	m_dc.DrawCircle( screenCenter, ( wxCoord ) screenRadius );
}

void WxWidgetsDrawer::DrawPolygon( const Polygon& polygon ) {
	int count = polygon.GetVerticesCount();
	wxPoint* points = new wxPoint[count];

	for( int i = 0; i < count; ++i ) {
		points[i] = m_transformer->WorldToScreen( polygon[i] );
	}

	m_dc.DrawPolygon( count, points );

	delete[] points;
}

void WxWidgetsDrawer::DrawModel( const Transform& position, const Model* model ) {

	Mesh* mesh = model->GetMesh();

	for( IteratorPtr<Face> facesIter( mesh->Faces ); !facesIter.IsEnd(); facesIter.Next() ) {
		Face face = facesIter.Current();

		int count = face.Vertices.GetLength();

		wxPoint* points = new wxPoint[count];

		for( int i = 0; i < count; ++i ) {
			Vec3 world3 = mesh->Positions[face.Vertices[i].PosIndex];
			Vec2 world2( world3.X, world3.Y );
			points[i] = m_transformer->WorldToScreen( world2 );
		}

		m_dc.DrawPolygon( count, points );

		delete[] points;
	}
}

Brush ToEditorBrush( const wxBrush& brush ) {
	return Brush( ToEditorColor( brush.GetColour() ), ToEditorBrushStyle( brush.GetStyle() ) );
}

wxBrush ToWxBrush( const Brush& brush ) {
	return wxBrush( ToWxColor( brush.GetColor() ), ToWxBrushStyle( brush.GetStyle() ) );
}

wxPen ToWxPen( const Pen& pen ) {
	return wxPen( ToWxColor( pen.GetColor() ), pen.GetWidth(), ToWxPenStyle( pen.GetStyle() ) );
}

Pen ToEditorPen( const wxPen& pen ) {
	return Pen( ToEditorColor( pen.GetColour() ), pen.GetWidth(), ToEditorPenStyle( pen.GetStyle() ) );
}

wxPenStyle ToWxPenStyle( PenStyle penStyle ) {
	switch( penStyle ) {
		case PenStyle::Solid:
			return wxPenStyle::wxPENSTYLE_SOLID;
		case PenStyle::Dashed:
			return wxPenStyle::wxPENSTYLE_SHORT_DASH;
		case PenStyle::DashDotted:
			return wxPenStyle::wxPENSTYLE_DOT_DASH;
		default:
			return wxPenStyle::wxPENSTYLE_INVALID;
	}
}

PenStyle ToEditorPenStyle( wxPenStyle penStyle ) {
	switch( penStyle ) {
		case wxPenStyle::wxPENSTYLE_SOLID:
			return PenStyle::Solid;
		case wxPenStyle::wxPENSTYLE_SHORT_DASH:
			return PenStyle::Dashed;
		case wxPenStyle::wxPENSTYLE_DOT_DASH:
			return PenStyle::DashDotted;
		default:
			return PenStyle::Unknown;
	}
}

wxBrushStyle ToWxBrushStyle( BrushStyle brushStyle ) {
	switch( brushStyle ) {
		case BrushStyle::Solid:
			return wxBrushStyle::wxBRUSHSTYLE_SOLID;
		case BrushStyle::Transparent:
			return wxBrushStyle::wxBRUSHSTYLE_TRANSPARENT;
		case BrushStyle::Stipple:
			return wxBrushStyle::wxBRUSHSTYLE_STIPPLE;
		case BrushStyle::BdiagonalHatch:
			return wxBrushStyle::wxBRUSHSTYLE_BDIAGONAL_HATCH;
		case BrushStyle::FdiagonalHatch:
			return wxBrushStyle::wxBRUSHSTYLE_FDIAGONAL_HATCH;
		case BrushStyle::CrossdiagHatch:
			return wxBrushStyle::wxBRUSHSTYLE_CROSSDIAG_HATCH;
		case BrushStyle::HorizontalHatch:
			return wxBrushStyle::wxBRUSHSTYLE_HORIZONTAL_HATCH;
		case BrushStyle::VerticalHatch:
			return wxBrushStyle::wxBRUSHSTYLE_VERTICAL_HATCH;
		case BrushStyle::CrossHatch:
			return wxBrushStyle::wxBRUSHSTYLE_CROSS_HATCH;
		default:
			return wxBrushStyle::wxBRUSHSTYLE_INVALID;
	}
}

BrushStyle ToEditorBrushStyle( wxBrushStyle brushStyle ) {
	switch( brushStyle ) {
		case wxBrushStyle::wxBRUSHSTYLE_SOLID:
			return BrushStyle::Solid;
		case wxBrushStyle::wxBRUSHSTYLE_TRANSPARENT:
			return BrushStyle::Transparent;
		case wxBrushStyle::wxBRUSHSTYLE_STIPPLE:
			return BrushStyle::Stipple;
		case wxBrushStyle::wxBRUSHSTYLE_BDIAGONAL_HATCH:
			return BrushStyle::BdiagonalHatch;
		case wxBrushStyle::wxBRUSHSTYLE_FDIAGONAL_HATCH:
			return BrushStyle::FdiagonalHatch;
		case wxBrushStyle::wxBRUSHSTYLE_CROSSDIAG_HATCH:
			return BrushStyle::CrossdiagHatch;
		case wxBrushStyle::wxBRUSHSTYLE_HORIZONTAL_HATCH:
			return BrushStyle::HorizontalHatch;
		case wxBrushStyle::wxBRUSHSTYLE_VERTICAL_HATCH:
			return BrushStyle::VerticalHatch;
		case wxBrushStyle::wxBRUSHSTYLE_CROSS_HATCH:
			return BrushStyle::CrossdiagHatch;
		default:
			return BrushStyle::Unknown;
	}
}

wxColor ToWxColor( const Color& color ) {
	return wxColor( ToChar( color.R ), ToChar( color.G ), ToChar( color.B ), ToChar( color.A ) );
}

Color ToEditorColor( const wxColor& color ) {
	return Color( ToFloat( color.Red() ), ToFloat( color.Green() ), ToFloat( color.Blue() ), ToFloat( color.Alpha() ) );
}

unsigned ToChar( float val ) {
	return unsigned( val * 255.0f );
}

float ToFloat( unsigned val ) {
	return float( val ) / 255.0f;
}

